package com.bookrecomm.crawler.service.impl;

import com.bookrecomm.crawler.entity.Book;
import com.bookrecomm.crawler.entity.Category;
import com.bookrecomm.crawler.entity.Tag;
import com.bookrecomm.crawler.service.AbstractBookCrawler;
import com.bookrecomm.crawler.util.HttpUtil;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ArXiv学术论文爬虫实现类
 */
@Slf4j
public class AcademicCrawler extends AbstractBookCrawler {

    // ArXiv列表页URL模板，参数为分类和起始索引
    private static final String LIST_URL_TEMPLATE = "https://arxiv.org/list/%s/recent?skip=%d&show=50";
    
    // 使用搜索API的URL模板（替代方案）
    private static final String SEARCH_URL_TEMPLATE = "https://arxiv.org/search/?query=%s&searchtype=all&start=%d&max_results=50";
    
    // ArXiv详情页URL模板
    private static final String DETAIL_URL_PREFIX = "https://arxiv.org/abs/";
    
    // 论文ID的正则表达式模式
    private static final Pattern PAPER_ID_PATTERN = Pattern.compile("\\d+\\.\\d+");
    
    // 允许的最大重试次数
    private static final int MAX_RETRIES = 3;
    
    // 常用学科分类代码
    private static final String[] COMMON_CATEGORIES = {
        "cs.AI",    // 人工智能
        "cs.CL",    // 计算语言学
        "cs.CV",    // 计算机视觉
        "cs.LG",    // 机器学习
        "cs.SE",    // 软件工程
        "math.ST",  // 统计学
        "physics"   // 物理学
    };
    
    @Override
    public List<String> crawlBookListPage(String categoryUrl, int pageNum) {
        Set<String> detailUrlSet = new HashSet<>(); // 使用Set避免重复
        int retryCount = 0;
        boolean success = false;
        String pageUrl = "";
        
        while (!success && retryCount < MAX_RETRIES) {
            try {
                // 计算分页跳过数量（每页显示50条）
                int skip = (pageNum - 1) * 50;
                
                // 尝试多种URL模板
                String[] urlTemplates = {
                    LIST_URL_TEMPLATE,  // 首先尝试列表页
                    SEARCH_URL_TEMPLATE // 然后尝试搜索页
                };
                
                for (String template : urlTemplates) {
                    if (!detailUrlSet.isEmpty()) {
                        break; // 如果已经找到论文，就不再尝试其他模板
                    }
                    
                    pageUrl = String.format(template, categoryUrl, skip);
                    log.info("尝试爬取ArXiv页面: {}", pageUrl);
                    
                    // 获取页面文档
                    Document doc = getDocument(pageUrl);
                    if (doc == null) {
                        log.error("获取页面文档失败: {}", pageUrl);
                        continue;
                    }
                    
                    log.info("成功获取页面，标题: {}", doc.title());
                    
                    // 策略1: 使用特定选择器查找论文列表
                    findPapersWithSpecificSelectors(doc, detailUrlSet);
                    
                    // 策略2: 使用正则表达式查找论文ID
                    findPapersWithRegex(doc.html(), detailUrlSet);
                    
                    // 策略3: 查找所有链接并筛选
                    findPapersFromAllLinks(doc, detailUrlSet);
                    
                    if (!detailUrlSet.isEmpty()) {
                        log.info("在页面 {} 中找到 {} 篇论文", pageUrl, detailUrlSet.size());
                        success = true;
                        break;
                    }
                }
                
                if (detailUrlSet.isEmpty()) {
                    retryCount++;
                    log.warn("未找到任何论文，重试次数: {}/{}", retryCount, MAX_RETRIES);
                    // 随机等待一段时间再重试
                    Thread.sleep(1000 + new Random().nextInt(2000));
                } else {
                    success = true;
                }
                
            } catch (Exception e) {
                log.error("爬取过程中发生异常: {}", e.getMessage(), e);
                retryCount++;
                try {
                    Thread.sleep(2000); // 出错后等待2秒再重试
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
        
        log.info("共发现{}篇ArXiv论文", detailUrlSet.size());
        log.info("学术列表页 {} 共找到 {} 本图书", pageUrl, detailUrlSet.size());
        
        // 随机等待，避免频繁请求
        HttpUtil.randomSleep(1, 3);
        
        return new ArrayList<>(detailUrlSet);
    }
    
    /**
     * 使用特定的CSS选择器查找论文
     */
    private void findPapersWithSpecificSelectors(Document doc, Set<String> detailUrls) {
        // 尝试多种可能的选择器
        String[] selectors = {
            ".arxiv-result", // 搜索页结果
            "dl.accordion", // 列表页结果
            "#dlpage .list-title", // 另一种列表页结构
            "#dlpage a[href^='/abs/']", // 直接查找指向/abs/的链接
            ".meta a[href^='/abs/']", // 元数据中的链接
            "#content a[href^='/abs/']", // 内容区域中的链接
            ".listing a[href^='/abs/']", // 列表中的链接
            "dt a[href^='/abs/']", // 定义列表中的链接
            "dd a[href^='/abs/']", // 定义列表描述中的链接
        };
        
        for (String selector : selectors) {
            Elements elements = doc.select(selector);
            log.debug("使用选择器 '{}' 找到 {} 个元素", selector, elements.size());
            
            for (Element element : elements) {
                // 如果元素本身是链接
                if (element.is("a")) {
                    extractPaperIdFromLink(element, detailUrls);
                } else {
                    // 查找元素内的链接
                    Elements links = element.select("a[href^='/abs/']");
                    for (Element link : links) {
                        extractPaperIdFromLink(link, detailUrls);
                    }
                }
            }
        }
    }
    
    /**
     * 使用正则表达式从HTML中提取论文ID
     */
    private void findPapersWithRegex(String html, Set<String> detailUrls) {
        // 查找形如 /abs/2106.12345 的模式
        Pattern pattern = Pattern.compile("/abs/(\\d+\\.\\d+)");
        Matcher matcher = pattern.matcher(html);
        
        while (matcher.find()) {
            String paperId = matcher.group(1);
            String detailUrl = DETAIL_URL_PREFIX + paperId;
            if (detailUrls.add(detailUrl)) { // 如果是新添加的
                log.debug("通过正则表达式找到论文: {}", detailUrl);
            }
        }
    }
    
    /**
     * 从所有链接中查找论文链接
     */
    private void findPapersFromAllLinks(Document doc, Set<String> detailUrls) {
        Elements allLinks = doc.select("a[href]");
        log.debug("页面上找到 {} 个链接", allLinks.size());
        
        for (Element link : allLinks) {
            extractPaperIdFromLink(link, detailUrls);
        }
    }
    
    /**
     * 从链接元素中提取论文ID并添加到结果集
     */
    private void extractPaperIdFromLink(Element link, Set<String> detailUrls) {
        String href = link.attr("href");
        
        // 检查是否是指向论文详情的链接
        if (href.contains("/abs/")) {
            try {
                // 提取论文ID
                String paperId = href.substring(href.lastIndexOf("/abs/") + 5);
                
                // 移除查询参数
                if (paperId.contains("?")) {
                    paperId = paperId.substring(0, paperId.indexOf("?"));
                }
                
                // 验证论文ID格式是否正确（通常是数字.数字格式）
                Matcher matcher = PAPER_ID_PATTERN.matcher(paperId);
                if (matcher.find()) {
                    String detailUrl = DETAIL_URL_PREFIX + paperId;
                    if (detailUrls.add(detailUrl)) { // 如果是新添加的
                        log.debug("发现ArXiv论文: {}", detailUrl);
                    }
                }
            } catch (Exception e) {
                log.debug("解析链接时出错: {}, 链接: {}", e.getMessage(), href);
            }
        }
    }

    @Override
    public Book crawlBookDetailPage(String url) {
        log.info("爬取学术图书详情页: {}", url);
        
        // 获取页面文档
        Document doc = getDocument(url);
        if (doc == null) {
            log.error("获取学术详情页文档失败: {}", url);
            return null;
        }
        
        Book book = new Book();
        
        try {
            // 提取标题
            Element titleElement = doc.selectFirst("h1.title");
            if (titleElement != null) {
                book.setTitle(titleElement.text().trim().replace("Title: ", ""));
            } else {
                // 尝试其他可能的标题选择器
                titleElement = doc.selectFirst(".title, .ltx_title_document");
                if (titleElement != null) {
                    book.setTitle(titleElement.text().trim());
                } else {
                    // 如果仍然找不到，设置一个默认标题
                    String paperId = url.substring(url.lastIndexOf("/") + 1);
                    book.setTitle("ArXiv论文: " + paperId);
                }
            }
            
            // 提取作者
            Elements authorElements = doc.select(".authors a, .ltx_creator_item");
            if (!authorElements.isEmpty()) {
                StringBuilder authors = new StringBuilder();
                for (Element author : authorElements) {
                    if (authors.length() > 0) {
                        authors.append(", ");
                    }
                    authors.append(author.text().trim());
                }
                book.setAuthor(authors.toString());
            } else {
                book.setAuthor("未知作者");
            }
            
            // 提取摘要作为描述
            Element abstractElement = doc.selectFirst(".abstract, .ltx_abstract");
            if (abstractElement != null) {
                String description = abstractElement.text().trim();
                if (description.startsWith("Abstract: ")) {
                    description = description.substring("Abstract: ".length());
                }
                book.setDescription(description);
            } else {
                book.setDescription("无摘要信息");
            }
            
            // 提取分类信息
            List<Category> categories = new ArrayList<>();
            Elements categoryElements = doc.select(".subjects a, .ltx_keywords .ltx_keyword");
            int level = 1;
            for (Element element : categoryElements) {
                String categoryName = element.text().trim();
                if (!categoryName.isEmpty()) {
                    Category category = new Category();
                    category.setName(categoryName);
                    category.setLevel(level++);
                    categories.add(category);
                }
            }
            book.setCategories(categories);
            
            // 提取关键词/标签
            List<Tag> tags = new ArrayList<>();
            // 从分类中提取关键词
            for (Element element : categoryElements) {
                String tagName = element.text().trim();
                if (!tagName.isEmpty()) {
                    Tag tag = new Tag();
                    tag.setName(tagName);
                    tag.setHeat(1); // 默认热度
                    tags.add(tag);
                }
            }
            book.setTags(tags);
            
            // 设置默认值
            book.setLanguage("英文");
            book.setPublisher("ArXiv.org");
            book.setIsbn("ArXiv:" + url.substring(url.lastIndexOf("/") + 1));
            book.setPrice(new BigDecimal("0.00"));
            book.setRating(new BigDecimal("4.5"));
            book.setRatingCount(100);
            book.setStatus(1);
            book.setCreatedAt(new Date());
            book.setUpdatedAt(new Date());
            
            log.info("爬取学术图书详情成功: {}", book.getTitle());
        } catch (Exception e) {
            log.error("爬取学术图书详情失败: {}", url, e);
        }
        
        // 随机等待，避免频繁请求
        HttpUtil.randomSleep(2, 5);
        
        return book;
    }

    @Override
    public List<String> crawlBookComments(String bookId, int pageNum) {
        // 学术平台通常没有评论，返回空列表
        return new ArrayList<>();
    }

    @Override
    public void startCrawl(String categoryUrl, int pageStart, int pageEnd) {
        log.info("开始爬取学术平台分类 {} 的图书，页码范围: {} - {}", categoryUrl, pageStart, pageEnd);
        
        int successCount = 0;
        int failCount = 0;
        
        for (int page = pageStart; page <= pageEnd; page++) {
            // 爬取列表页
            List<String> detailUrls = crawlBookListPage(categoryUrl, page);
            
            // 爬取详情页
            for (String detailUrl : detailUrls) {
                Book book = crawlBookDetailPage(detailUrl);
                if (book != null) {
                    // 保存图书数据
                    boolean success = saveBook(book);
                    if (success) {
                        successCount++;
                        log.info("成功保存学术图书: {}", book.getTitle());
                    } else {
                        failCount++;
                        log.error("保存学术图书失败: {}", book.getTitle());
                    }
                } else {
                    failCount++;
                }
            }
            
            log.info("完成学术平台第 {} 页爬取，成功: {}，失败: {}", page, successCount, failCount);
        }
        
        log.info("学术平台分类 {} 爬取完成，总成功: {}，总失败: {}", categoryUrl, successCount, failCount);
    }
}
